home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-12-10 | 39.9 KB | 1,723 lines | [TEXT/KAHL] |
- /*
- ** CTCPStream.cp
- **
- ** TurboTCP support library
- ** TCP stream class
- **
- ** Copyright © 1993, FrostByte Design / Eric Scouten
- **
- */
-
-
- #include "CTCPStream.h"
-
- #ifndef TurboTCPHeaders
- #include <TCLUtilities.h>
- #include <CList.h>
- #include "TurboTCP.const.h"
- #endif
-
- #include "CTCPAsyncCall.h"
- #include "CTCPDriver.h"
-
-
- // —— initialize/destroy stream ——
-
- /*______________________________________________________________________
- **
- ** ITCPStream
- **
- ** Create a new TCP stream. Allocates the receive buffer (if there’s enough memory).
- **
- ** The stream can be configured to automatically receive data. If auto-receive is used,
- ** the stream object will automatically issue TCPNoCopyRcv calls as needed and respond to
- ** completion notifications when data arrives; data will be returned by the HandleDataArrived
- ** method or by the receive bypass procedure (see InstallRcvBypassProc).
- **
- ** If auto-receive is used, you should not send NoCopyRcv and Rcv messages to this stream.
- ** They may conflict with the auto-receive calls. Auto-receive cannot be turned on or off
- ** once the stream object is created.
- **
- ** recBufferSize (long): size of the receive buffer we need; use 0 or
- ** less then 4K to get the minimum 4K buffer
- ** autoReceiveSize (short): number of entries in RDS for auto-receive,
- ** 0 to disable autoreceiving
- ** autoReceiveNum (short): number of auto-receive calls to issue at once
- ** must be at least 1
- **
- */
-
- void CTCPStream::ITCPStream (long recBufferSize, short autoReceiveSize, short autoReceiveNum)
-
- {
-
- // clear all of our variables
-
- macTCPStream = NULL;
- itsBuffer = NULL;
- itsAsyncCalls = NULL;
- hasSessionOpen = pendingOpen = pendingClose = remoteClose
- = pendingAbort = pendingNotify = pendingDispose = FALSE;
-
- itsBufferSize = 0L;
- rcvUrgent = FALSE;
- itsRcvBypassProc = NULL;
- itsRcvBypassTarget = NULL;
-
- itsULPtimeout = 0;
- itsULPaction = 0;
- itsValidityFlag = 0;
- itsCommandTimeoutValue = 0;
- itsTosFlags = 0;
- itsPrecedence = 0;
- itsDontFrag = 0;
- itsTimeToLive = 0;
- itsSecurity = 0;
- itsOptionCnt = 0;
- sendNextUrgent = 0;
- sendNextPush = FALSE;
-
- notifClosing = FALSE;
- notifTimeout = FALSE;
- notifTerminate = FALSE;
- notifDataArrived = FALSE;
- notifUrgent = FALSE;
- notifICMP = FALSE;
- disposeOnTerminate = FALSE;
- receivedClose = FALSE;
- receivedTerminate = FALSE;
- itsAutoReceiveSize = Min(autoReceiveSize, autoReceiveMax);
- itsAutoReceiveNum = Max(autoReceiveNum, 1);
-
-
- // prepare the async events queues
-
- qNotifyEntry.qType = notifyStream;
- qNotifyEntry.qSelfLink = this;
- qDisposeEntry.qType = disposeStream;
- qDisposeEntry.qSelfLink = this;
-
-
- // initialize the collaborator object
-
- CCollaborator::ICollaborator();
-
-
- // create the async calls list
-
- itsAsyncCalls = new (CCluster);
- itsAsyncCalls->ICluster();
-
-
- // create the receive buffer
-
- if (recBufferSize < minReceiveSize) // if caller requested a buffer that is too small,
- recBufferSize = minReceiveSize; // beef it up to acceptable size (4K)
- itsBuffer = NewHandle(recBufferSize);
- FailNIL(itsBuffer);
- itsBufferSize = recBufferSize;
-
-
- /*
- ** Don’t create TCP stream here. Therefore, if a session is never opened and the program
- ** crashes (or ExitToShell is used by the programmer), MacTCP will remain stable.
- */
-
- }
-
-
- /*______________________________________________________________________
- **
- ** Dispose
- **
- ** Get rid of a TCP stream. Deallocates the receive buffer. If a session is still open,
- ** issues an Abort command, then postpones its disposal so completion routines still
- ** have a valid object to call.
- **
- */
-
- void CTCPStream::Dispose (void)
-
- {
- TCPiopb theReleaseParam;
-
-
- // we’re no longer in delayed disposal queue
-
- pendingDispose = FALSE;
-
-
- // initiate a graceful close if necessary
-
- if (hasSessionOpen && !receivedClose) {
- Close();
- disposeOnTerminate = TRUE;
- return;
- }
-
-
- // abort any connections that are yet to open
-
- if (pendingOpen) {
- Abort();
- disposeOnTerminate = TRUE;
- return;
- }
-
-
- // if calls are still outstanding, wait a while
-
- if (!itsAsyncCalls->IsEmpty()) {
- if ((macTCPStream) && pendingOpen) { // get rid of the thing…
- Abort();
- PostponeDispose();
- return;
- }
- if ((macTCPStream) && pendingOpen)
- DoSyncCall(TCPRelease, &theReleaseParam); // perhaps this will shake it loose
- PostponeDispose();
- return;
- }
-
-
- // if connection is still there, release stream to see if that shakes things loose
-
- if (macTCPStream && hasSessionOpen && !receivedTerminate) {
- DoSyncCall(TCPRelease, &theReleaseParam);
- macTCPStream = NULL;
- PostponeDispose();
- return;
- }
-
-
- // make sure that terminate notice has been received
-
- if (hasSessionOpen && !receivedTerminate) {
- PostponeDispose();
- return;
- }
-
-
- // release the stream from MacTCP
-
- if (macTCPStream)
- DoSyncCall(TCPRelease, &theReleaseParam);
- gTCPDriver->RemoveActiveStream(this);
-
-
- // forget the async calls list
-
- ForgetObject(itsAsyncCalls);
-
-
- // release the receive buffer
-
- ForgetHandle(itsBuffer);
- macTCPStream = NULL;
- itsBufferSize = 0L;
-
- CCollaborator::Dispose();
- }
-
-
- /*______________________________________________________________________
- **
- ** OwnerDied
- **
- ** Called when stream’s owner is being disposed and no longer wishes to receive
- ** notifications.
- **
- */
-
- void CTCPStream::OwnerDied (void)
-
- {
- InstallRcvBypass(NULL, NULL);
- }
-
-
- // —— basic user TCP calls ——
-
- /*______________________________________________________________________
- **
- ** OpenConnection
- **
- ** Opens a TCP session (either passive or active). Note that MacTCP seems to fail when
- ** non-zero values are used for theRemoteIP and theRemotePort.
- **
- ** passive (Boolean): TRUE for passive open; FALSE for active open
- ** theRemoteIP (ip_addr): IP address of remote host (0 for any host)
- ** theRemotePort (b_16): TCP port of remote host (0 for any port)
- ** theLocalPort (b_16): TCP port for local host (0 for any port)
- **
- */
-
- void CTCPStream::OpenConnection (Boolean passive, ip_addr theRemoteIP,
- b_16 theRemotePort, b_16 theLocalPort)
-
- {
- TCPiopb theCreateParam;
- TCPiopb theOpenParam;
- OSErr theResult;
-
-
- // ensure that MacTCP is awake & ready for us
-
- if (!(gTCPDriver->CheckTCPDriver()))
- FailOSErr(noTCPError);
-
-
- // if the stream hasn’t been created yet, create one now
-
- if (!macTCPStream) {
- MoveHHi(itsBuffer); // lock the receive buffer
- HLock(itsBuffer);
-
- MoveHHi((Handle) this); // prevent ASR calls while Memory Manager
- this->Lock(TRUE); // is moving this object
-
- theCreateParam.csParam.create.rcvBuff = *itsBuffer;
- theCreateParam.csParam.create.rcvBuffLen = itsBufferSize;
- theCreateParam.csParam.create.notifyProc = &NotifyProc;
- theCreateParam.csParam.create.userDataPtr = (Ptr) this;
-
- macTCPStream = (Ptr) 1; // preempt the check for no stream present
- theResult = DoSyncCall(TCPCreate, &theCreateParam);
-
- if (theResult == noErr) { // did we get a valid stream?
- gTCPDriver->RegisterActiveStream(this); // yes, register in global stream list
- macTCPStream = (Ptr) theCreateParam.tcpStream;
- } else {
- macTCPStream = NULL;
- HandleOpenFailed(theResult);
- }
- }
-
-
- // don’t allow open if already opening
-
- if (!(hasSessionOpen || pendingOpen)) {
-
- // fill in parms to open call
-
- theOpenParam.csParam.open.ulpTimeoutValue = itsULPtimeout;
- theOpenParam.csParam.open.ulpTimeoutAction = itsULPaction;
- theOpenParam.csParam.open.validityFlags = itsValidityFlag;
- theOpenParam.csParam.open.commandTimeoutValue = itsCommandTimeoutValue;
- theOpenParam.csParam.open.remoteHost = theRemoteIP;
- theOpenParam.csParam.open.remotePort = theRemotePort;
- theOpenParam.csParam.open.localPort = theLocalPort;
- theOpenParam.csParam.open.tosFlags = itsTosFlags;
- theOpenParam.csParam.open.precedence = itsPrecedence;
- theOpenParam.csParam.open.dontFrag = itsDontFrag;
- theOpenParam.csParam.open.timeToLive = itsTimeToLive;
- theOpenParam.csParam.open.security = itsSecurity;
- theOpenParam.csParam.open.optionCnt = itsOptionCnt;
- BlockMove(&itsOptions, &theOpenParam.csParam.open.options, 40);
- theOpenParam.csParam.open.userDataPtr = (Ptr) this;
- pendingOpen = TRUE;
- receivedClose = receivedTerminate = FALSE;
-
- // perform the call
-
- theResult = DoAsyncCall((passive ? TCPPassiveOpen : TCPActiveOpen),
- &theOpenParam);
- if (theResult)
- HandleOpenFailed(theResult);
-
- // pull IP address/port info from remote host
-
- itsRemoteIP = theOpenParam.csParam.open.remoteHost;
- itsRemotePort = theOpenParam.csParam.open.remotePort;
- itsLocalIP = theOpenParam.csParam.open.localHost;
- itsLocalPort = theOpenParam.csParam.open.localPort;
- }
- else
- HandleOpenFailed(connectionExists);
-
- }
-
-
- /*______________________________________________________________________
- **
- ** Close
- **
- ** Signal that the connection should be closed. The stream’s owner should wait until it
- ** receives the tcpStreamClosed message before disposing of the stream object.
- ** This will allow time for the host to close gracefully.
- **
- */
-
- void CTCPStream::Close (void)
-
- {
- TCPiopb theCloseParam;
- OSErr theResult;
-
- if ((hasSessionOpen && (!pendingAbort) && (!pendingClose)) || remoteClose) {
- pendingClose = TRUE;
- pendingOpen = remoteClose = FALSE;
- theCloseParam.csParam.close.ulpTimeoutValue = itsULPtimeout;
- theCloseParam.csParam.close.ulpTimeoutAction = itsULPaction;
- theCloseParam.csParam.close.validityFlags = itsValidityFlag;
- theCloseParam.csParam.close.userDataPtr = (Ptr) this;
- theResult = DoAsyncCall(TCPClose, &theCloseParam);
- if ((theResult != noErr) && (theResult != connectionDoesntExist))
- HandleTCPError(theResult, TCPClose);
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** Abort
- **
- ** Cancel the connection immediately. This call is performed synchronously. USE THIS CALL
- ** WITH CAUTION! MacTCP seems to get quite confused when a session is closed and
- ** data hasn’t all been received.
- **
- */
-
- void CTCPStream::Abort (void)
-
- {
- TCPiopb theAbortParam;
- OSErr theResult;
-
- if ((hasSessionOpen || pendingOpen) && (!pendingAbort)) {
- pendingAbort = TRUE;
- pendingOpen = pendingClose = remoteClose = FALSE;
- theAbortParam.csParam.abort.userDataPtr = (Ptr) this;
- theResult = DoSyncCall(TCPAbort, &theAbortParam);
- if (theResult)
- HandleTCPError(theResult, TCPAbort);
- else
- receivedClose = TRUE;
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** NoCopyRcv
- **
- ** Receive data without copying from TCP’s internal buffers. The completion routine in
- ** CTCPAsyncCall will take care of returning the RDS automatically. IF YOU ARE USING
- ** AUTO-RECEIVE, DO NOT CALL THIS METHOD!
- **
- ** itsRDS (rdsEntry *): receive data structure (see MacTCP manual, p30)
- ** itsRDSSize (b_16): number of entries in RDS (6 bytes each)
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- **
- */
-
- void CTCPStream::NoCopyRcv (rdsEntry *itsRDS, b_16 itsRDSSize, b_16 itsTimeOut)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
- theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
- theRcvParam.csParam.receive.rdsPtr = (Ptr) itsRDS;
- theRcvParam.csParam.receive.rdsLength = (unsigned short) Min(itsRDSSize, autoReceiveMax);
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
- theRcvParam.csParam.open.options[36] = (Byte) itsAutoReceiveSize;
-
- theResult = DoAsyncCall(TCPNoCopyRcv, &theRcvParam);
- if ((theResult != noErr) && (theResult != connectionClosing))
- HandleTCPError(theResult, TCPNoCopyRcv);
-
- }
-
-
- /*______________________________________________________________________
- **
- ** BfrReturn
- **
- ** Return a receive buffer to MacTCP used by the NoCopyRcv method. You should never need
- ** to call this method, since it is done automatically by the CTCPAsyncCall::Dispatch()
- ** method which handles all asynchronous completions.
- **
- ** itsRDS (Ptr): the RDS structure to return
- **
- */
-
- void CTCPStream::BfrReturn (Ptr itsRDS)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
-
- // reject attempt to return buffers after the session is closed…
- // MacTCP has done it already (I think)
-
- if (hasSessionOpen) {
- theRcvParam.csParam.receive.rdsPtr = itsRDS;
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
-
- DoSyncCall(TCPRcvBfrReturn, &theRcvParam);
- // ignore errors — this is bad practice, but…
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** Send
- **
- ** Send data on the TCP stream using the Write Data Structure (WDS) structure. The
- ** asynchronous completion routine can optionally dispose of the WDS and its associated data
- ** structures once the write operation is completed. If so, the buffers you provide must be
- ** expendable.
- **
- ** itsWDS (wdsEntry *): write data structure (see MacTCP manual, p30)
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- ** disposeWDS (Boolean): dispose of WDS and data structures when completed
- **
- */
-
- void CTCPStream::Send (wdsEntry *itsWDS, b_16 itsTimeOut, Boolean disposeWDS)
-
- {
- TCPiopb theSendParam;
- OSErr theResult;
-
-
- // ensure that at least one byte is being sent
-
-
- if ((*itsWDS).length == 0)
- return;
-
-
- // fill in parms to send call
-
- theSendParam.csParam.send.ulpTimeoutValue = itsTimeOut;
- theSendParam.csParam.send.ulpTimeoutAction = itsULPaction;
- theSendParam.csParam.send.validityFlags = itsValidityFlag;
- theSendParam.csParam.send.pushFlag = sendNextPush;
- theSendParam.csParam.send.urgentFlag = sendNextUrgent;
- theSendParam.csParam.send.wdsPtr = (Ptr) itsWDS;
- theSendParam.csParam.send.userDataPtr = (Ptr) this;
-
- theSendParam.csParam.open.security = disposeWDS;
- // ^^^^ this isn’t pretty, but it’s efficient
-
- sendNextPush = FALSE;
- sendNextUrgent = 0;
-
-
- // perform the call
-
- theResult = DoAsyncCall(TCPSend, &theSendParam);
- if (theResult)
- HandleTCPError(theResult, TCPSend);
- }
-
-
- /*______________________________________________________________________
- **
- ** Receive
- **
- ** Receive data and copy to a user buffer.
- **
- ** theData (Ptr): receive buffer
- ** theDataSize (b_16): size of receive buffer in bytes
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- **
- */
-
- void CTCPStream::Receive (Ptr theData, b_16 itsDataSize, b_16 itsTimeOut)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
- theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
- theRcvParam.csParam.receive.rcvBuff = theData;
- theRcvParam.csParam.receive.rcvBuffLen = (unsigned short) itsDataSize;
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
- theRcvParam.csParam.open.userDataPtr = (Ptr) itsRcvBypassProc;
- // hide the extra data in unused fields
-
- theResult = DoAsyncCall(TCPRcv, &theRcvParam);
- if (theResult)
- HandleTCPError(theResult, TCPRcv);
- }
-
-
- /*______________________________________________________________________
- **
- ** Status
- **
- ** Returns a large pile of information about the TCP connection.
- **
- ** theStatusBlock (TCPStatusPB *): user buffer for TCP status info
- **
- */
-
- void CTCPStream::Status (TCPStatusPB *theStatusBlock)
-
- {
- TCPiopb theStatusParam;
- short i;
- Ptr p;
-
- DoSyncCall(TCPStatus, &theStatusParam);
- BlockMove(&theStatusParam.csParam, theStatusBlock, 66);
- if (theStatusParam.ioResult != noErr) {
- i = 0;
- p = (Ptr) theStatusBlock;
- while (i++<66)
- *(p++) = 0;
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** ConnectionState
- **
- ** Returns the current status of the MacTCP stream. For definitions of the state codes,
- ** see TCP reference manual, p56.
- **
- ** return (b_16): status of TCP stream
- **
- */
-
- b_16 CTCPStream::ConnectionState (void)
-
- {
- TCPStatusPB theStatusBlock;
-
- Status(&theStatusBlock);
- return(theStatusBlock.connectionState);
- }
-
-
- // —— specialized functions for sending data ——
-
- /*______________________________________________________________________
- **
- ** SendChar
- **
- ** Send a single character to the TCP stream.
- **
- ** theChar (char): the character to send
- **
- */
-
- void CTCPStream::SendChar (char theChar)
-
- {
- SendBfrCpy(&theChar, 1);
- }
-
-
- /*______________________________________________________________________
- **
- ** SendCString
- **
- ** Send a C string to the TCP stream.
- **
- ** theString (char *): the string to send
- **
- */
-
- void CTCPStream::SendCString (char *theString)
-
- {
- SendBfrCpy(theString, cstrlen(theString));
- }
-
-
- /*______________________________________________________________________
- **
- ** SendPString
- **
- ** Send a Pascal string to the TCP stream.
- **
- ** theString (Str255): the string to send
- **
- */
-
- void CTCPStream::SendPString (ConstStr255Param theString)
-
- {
- SendBfrCpy((char *) theString + 1, theString[0]);
- }
-
-
- /*______________________________________________________________________
- **
- ** SendBfrCpy
- **
- ** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method and copies
- ** the bytes from the caller’s buffer to a data buffer which can persist beyond the procedure
- ** or object which generated the data.
- **
- ** theData (Ptr): the bytes to send
- ** theDataSize (b_16): number of bytes to send
- **
- */
-
- void CTCPStream::SendBfrCpy (Ptr theData, b_16 theDataSize)
-
- {
- wdsEntry *itsWDS = NULL;
- Ptr itsNewBuffer = NULL;
-
-
- // make sure there’s something to send
-
- if (theDataSize == 0)
- return;
-
- TRY {
-
- // create the two buffers
-
- itsWDS = (wdsEntry *) NewPtr(8); // WDS with room for one entry
- itsNewBuffer = NewPtr(theDataSize);
-
-
- // copy stuff to new buffer
-
- BlockMove(theData, itsNewBuffer, theDataSize);
- (*itsWDS).length = theDataSize;
- (*itsWDS).ptr = itsNewBuffer;
- (*(++itsWDS)).length = 0;
- --itsWDS;
-
-
- // send data, using default timeout value
-
- Send(itsWDS, itsULPtimeout, TRUE);
-
- }
-
- CATCH {
- ForgetPtr(itsWDS);
- ForgetPtr(itsNewBuffer);
- }
- ENDTRY;
-
- }
-
-
- /*______________________________________________________________________
- **
- ** SendBfrNoCpy
- **
- ** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method. Does not
- ** copy the bytes.
- **
- ** theData (Ptr): the bytes to send
- ** theDataSize (b_16): number of bytes to send
- ** disposeWhenDone (Boolean): dispose of the buffer when completed — set to TRUE
- ** only if this pointer was allocated as a pointer,
- ** not a handle deref
- **
- */
-
- void CTCPStream::SendBfrNoCpy (Ptr theData, b_16 theDataSize, Boolean disposeWhenDone)
-
- {
- wdsEntry *itsWDS = NULL;
-
-
- // make sure there’s something to send
-
- if (theDataSize == 0)
- return;
-
- TRY {
-
- // create the WDS buffer & fill it in
-
- itsWDS = (wdsEntry *) NewPtr(8); // WDS with room for one entry
-
- (*itsWDS).length = theDataSize;
- (*itsWDS).ptr = theData;
- (*(++itsWDS)).length = 0;
- --itsWDS;
-
-
- // send data, using default timeout value
-
- Send(itsWDS, itsULPtimeout, disposeWhenDone);
- }
-
- CATCH {
- ForgetPtr(itsWDS);
- }
- ENDTRY;
-
- }
-
-
- /*______________________________________________________________________
- **
- ** SetNextPush
- **
- ** Use before any send-data command to make sure that data is sent with the “push” flag set.
- **
- */
-
- void CTCPStream::SetNextPush (void)
-
- {
- sendNextPush = TRUE;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetNextUrgent
- **
- ** Use before any send-data command to make sure that data is sent with the “urgent”
- ** flag set.
- **
- ** useRFC793 (Boolean): TRUE to use the non-compliant method in RFC 793
- **
- */
-
- void CTCPStream::SetNextUrgent (Boolean useRFC793)
-
- {
- sendNextUrgent = (useRFC793 ? 2 : 1);
- }
-
-
- // —— notification routines
-
- /*______________________________________________________________________
- **
- ** HandleClosed
- **
- ** Respond to notification that the session has closed.
- **
- */
-
- void CTCPStream::HandleClosed (void)
-
- {
- BroadcastSafeChange(tcpStreamClosed, NULL);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleClosing
- **
- ** Respond to notification that the session is being closed.
- **
- ** remoteClosing (Boolean): TRUE if close initiated by remote host
- **
- */
-
- void CTCPStream::HandleClosing (Boolean remoteClosing)
-
- {
- BroadcastSafeChange(tcpStreamClosing, (void *) remoteClosing);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleDataArrived (should only be called by CTCPAsyncCall::Dispatch)
- **
- ** Process notification that data has arrived.
- **
- ** theData (Ptr): pointer to the data
- ** theDataSize (b_16): size of data
- ** isUrgent (Boolean): TRUE if remote host signalled TCP urgent data
- **
- */
-
- void CTCPStream::HandleDataArrived (Ptr theData, b_16 theDataSize, Boolean isUrgent)
-
- {
- DataArrivedInfo info;
-
- info.theData = theData;
- info.theDataSize = theDataSize;
- info.isUrgent = isUrgent;
-
- BroadcastChange(tcpStreamDataArrived, &info);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleDataSent
- **
- ** Respond to notification that data has been successfully sent.
- **
- ** WDSPtr (wdsEntry *): the WDS structure for the data that was sent
- **
- */
-
- void CTCPStream::HandleDataSent (wdsEntry *WDSPtr)
-
- {
- BroadcastChange(tcpStreamDataSent, WDSPtr);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleICMP
- **
- ** Respond to an ICMP message which was received.
- **
- ** icmpMsg (struct ICMPReport *): the ICMP message report
- **
- */
-
- void CTCPStream::HandleICMP (struct ICMPReport *icmpMsg)
-
- {
- BroadcastChange(tcpStreamICMP, icmpMsg);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleOpened
- **
- ** Respond to successful completion of a TCPPassiveOpen or TCPActiveOpen command.
- **
- */
-
- void CTCPStream::HandleOpened (void)
-
- {
- short i;
-
-
- // let everyone know we’ve got an active session
-
- hasSessionOpen = TRUE;
- pendingOpen = FALSE;
- BroadcastChange(tcpStreamOpened, NULL);
-
-
- // issue all the auto-receive commands needed
-
- for (i=1; i<=itsAutoReceiveNum; i++)
- IssueAutoReceive();
-
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleOpenFailed
- **
- ** Respond to notification that a TCPOpen command failed.
- **
- ** theResultCode (OSErr): the reason for failure
- **
- */
-
- void CTCPStream::HandleOpenFailed (OSErr theResultCode)
-
- {
- hasSessionOpen = FALSE;
- pendingOpen = FALSE;
-
- BroadcastSafeChange(tcpStreamOpenFailed, (void *) theResultCode);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleSendFailed
- **
- ** Respond to failure to send data. The WDS is returned so the data may be re-sent. After
- ** the dependents are notified, this method frees the WDS and the data buffers.
- **
- ** WDSPtr (wdsEntry *): the WDS structure
- ** theResultCode (OSErr): the reason for failure
- **
- */
-
- void CTCPStream::HandleSendFailed (wdsEntry *WDSPtr, OSErr theResultCode)
-
- {
- SendFailedInfo info;
- wdsEntry *theDisposePtr = WDSPtr;
-
-
- // package up the information for dependent objects
-
- info.WDSPtr = WDSPtr;
- info.theResultCode = theResultCode;
- BroadcastSafeChange(tcpStreamSendFailed, &info);
-
-
- // free the buffers holding the unsent data
-
- while ((*theDisposePtr).length) {
- ForgetPtr((*theDisposePtr).ptr);
- theDisposePtr++;
- }
- ForgetPtr(WDSPtr);
-
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleTCPError
- **
- ** Respond to failure of a TCP command that was not expected. Sends notification to
- ** dependent objects.
- **
- ** theResultCode (OSErr): the result code
- ** theCsCode (short): the TCP command number that failed
- **
- */
-
- void CTCPStream::HandleTCPError (OSErr theResultCode, short theCsCode)
-
- {
- TCPErrorInfo info;
-
- info.theResultCode = theResultCode;
- info.theCsCode = theCsCode;
- BroadcastSafeChange(tcpStreamTCPError, &info);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleTerminated
- **
- ** Respond to session termination event.
- **
- ** terminReason (b_16): the termination code
- **
- */
-
- void CTCPStream::HandleTerminated (b_16 terminReason)
-
- {
- TerminatedInfo info;
-
- info.terminReason = terminReason;
- info.aboutToDispose = pendingDispose || disposeOnTerminate;
- BroadcastSafeChange(tcpStreamTerminated, &info);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleTimeout
- **
- ** Respond to a ULP timeout event (remote TCP failed to respond in time). This method is
- ** only called if the ULP timeout action is set to abort & notify.
- **
- */
-
- void CTCPStream::HandleTimeout (void)
-
- {
- BroadcastSafeChange(tcpStreamTimeout, NULL);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleUnexpectedData
- **
- ** Respond to notification that data arrived without a receive command.
- **
- */
-
- void CTCPStream::HandleUnexpectedData (void)
-
- {
- BroadcastChange(tcpStreamUnexpectedData, NULL);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleUrgentBegin
- **
- ** Respond to notification that urgent data is arriving.
- **
- */
-
- void CTCPStream::HandleUrgentBegin (void)
-
- {
- BroadcastChange(tcpStreamUrgentBegin, NULL);
- }
-
-
- // —— set configuration ——
-
- /*______________________________________________________________________
- **
- ** InstallRcvBypass
- **
- ** Install a receive “bypass” routine. This routine may be used to accelerate the processing
- ** of arriving data by bypassing several method dispatches.
- **
- ** A bypass routine is called by the CTCPAsyncCall::Dispatch routine for each block of data.
- ** This call is made in place of the call to CTCPStream::HandleDataArrived. The routine
- ** should make a class-specific call to your object’s routine for processing data with the
- ** same parameters provided to it. (See example below.)
- **
- ** typedef void (*RcvBypassProc) (CDirectorOwner *theObject, Ptr theData,
- ** b_16 theDataSize, Boolean isUrgent);
- **
- ** {
- ** ((CYourClass *) theObject)->HandleDataArrived(…);
- ** }
- **
- **
- ** input (to InstallRcvBypass):
- ** aRcvBypassProc (RcvBypassProc): the receive bypass procedure for this stream
- **
- */
-
- struct BypassTargetRec {
- CObject *target;
- RcvBypassProc proc;
- };
-
- void CTCPStream::InstallRcvBypass (CObject *whoToCall, RcvBypassProc aRcvBypassProc)
-
- {
- struct BypassTargetRec s;
-
- s.target = itsRcvBypassTarget = whoToCall;
- s.proc = itsRcvBypassProc = aRcvBypassProc;
- itsAsyncCalls->DoForEach1(&GiveBypassToAsyncCall, (long) &s);
- }
-
- // private static method for iteration
-
- void CTCPStream::GiveBypassToAsyncCall (CObject *theObject, long param)
- {
- ((CTCPAsyncCall *) theObject)->
- SetRcvBypassProc(((struct BypassTargetRec *) param)->target,
- ((struct BypassTargetRec *) param)->proc);
- }
-
-
- /*______________________________________________________________________
- **
- ** SetULPTimeoutValue
- **
- ** Sets the ULP timeout value. The value given here applies to all subsequent open and
- ** send calls.
- **
- ** ulpTimeoutValue (b_16): timeout value (seconds, 0 = infinite)
- **
- */
-
- void CTCPStream::SetULPTimeoutValue (b_16 ulpTimeoutValue)
-
- {
- itsULPtimeout = ulpTimeoutValue;
- itsValidityFlag |= timeoutValue;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetULPTimeoutAction
- **
- ** Sets the ULP timeout value. This call is only useful when a connection has not been opened
- ** on this stream. (Otherwise, the value applies to subsequent connections on the same
- ** stream.)
- **
- ** ulpTimeoutAction (b_16): ULP timeout action (0 = report, nonzero = abort)
- **
- */
-
- void CTCPStream::SetULPTimeoutAction (b_16 ulpTimeoutAction)
-
- {
- itsULPaction = ulpTimeoutAction;
- itsValidityFlag |= timeoutAction;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetCommandTimeout
- **
- ** Sets the command timeout value. This call is only useful when a connection has not been
- ** opened on this stream. (Otherwise, the value applies to subsequent connections on the
- ** same stream.)
- **
- ** cmdTimeoutValue (b_16): command timeout value in seconds
- **
- */
-
- void CTCPStream::SetCommandTimeout (b_16 cmdTimeoutValue)
-
- {
- itsCommandTimeoutValue = cmdTimeoutValue;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetTypeOfService
- **
- ** Sets the type of service flag. This call is only useful when a connection has not been
- ** opened on this stream. (Otherwise, the value applies to subsequent connections on the
- ** same stream.)
- **
- ** newTosFlag (b_16): type-of-service value
- **
- */
-
- void CTCPStream::SetTypeOfService (b_16 newTosFlag)
-
- {
- itsTosFlags = newTosFlag;
- itsValidityFlag |= typeOfService;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetPrecedence
- **
- ** Sets the precedence flag. This call is only useful when a connection has not been opened on
- ** this stream. (Otherwise, the value applies to subsequent connections on the same stream.)
- **
- ** newPrecedence (b_16): the precedence flag
- **
- */
-
- void CTCPStream::SetPrecedence (b_16 newPrecedence)
-
- {
- itsPrecedence = newPrecedence;
- itsValidityFlag |= precedence;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetDontFrag
- **
- ** Sets the don’t fragment flag. This call is only useful when a connection has not been
- ** opened on this stream. (Otherwise, the value applies to subsequent connections on the
- ** same stream.)
- **
- ** newDontFrag (b_16): TRUE to prevent fragmentation of data segments
- **
- */
-
- void CTCPStream::SetDontFrag (b_16 newDontFrag)
-
- {
- itsDontFrag = newDontFrag;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetTimeToLive
- **
- ** Sets the ULP timeout value. This call is only useful when a connection has not been
- ** opened on this stream. (Otherwise, the value applies to subsequent connections on the
- ** same stream.)
- **
- ** newTimeToLive (b_16): time to live value (seconds)
- **
- */
-
- void CTCPStream::SetTimeToLive (b_16 newTimeToLive)
-
- {
- itsTimeToLive = newTimeToLive;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetSecurity
- **
- ** Sets the security flag. This call is only useful when a connection has not been opened on
- ** this stream. (Otherwise, the value applies to subsequent connections on the same stream.)
- **
- ** newSecurity (b_16): security flag value
- **
- */
-
- void CTCPStream::SetSecurity (b_16 newSecurity)
-
- {
- itsSecurity = newSecurity;
- }
-
-
- /*______________________________________________________________________
- **
- ** SetIPOptions
- **
- ** Sets the IP options. This call is only useful when a connection has not been opened on this
- ** stream. (Otherwise, the value applies to subsequent connections on the same stream.)
- **
- ** newOptionsSize (b_16): number of bytes in IP options string
- ** newOptions (IPOptionString *): the IP options bytes
- **
- */
-
- void CTCPStream::SetIPOptions (b_16 newOptionsSize, IPOptionString *newOptions)
-
- {
- Boolean wasLocked = this->Lock(TRUE);
-
- itsOptionCnt = newOptionsSize;
- BlockMove(newOptions, &itsOptions, IPOptionStringSize);
- this->Lock(wasLocked);
- }
-
-
- // —— TCP urgent mode ——
-
- /*______________________________________________________________________
- **
- ** RcvUrgentStatus
- **
- ** Return urgent mode status of stream.
- **
- ** return (Boolean): TRUE if stream is receiving urgent data
- **
- */
-
- Boolean CTCPStream::RcvUrgentStatus (void)
-
- {
- return(rcvUrgent);
- }
-
-
- /*______________________________________________________________________
- **
- ** RcvUrgentBegin (should only be called by TCP completion routine)
- **
- ** Signal beginning of urgent data.
- **
- */
-
- void CTCPStream::RcvUrgentBegin (void)
-
- {
- rcvUrgent = TRUE;
- }
-
-
- /*______________________________________________________________________
- **
- ** RcvUrgentMark (should only be called by TCP completion routine)
- **
- ** Signal end of urgent data.
- **
- */
-
- void CTCPStream::RcvUrgentMark (void)
-
- {
- rcvUrgent = FALSE;
- }
-
-
- // —— protected methods to issue TCP calls ——
-
- /*______________________________________________________________________
- **
- ** DoAsyncCall (protected method)
- **
- ** Creates a CTCPAsyncCall object and uses it to execute a TCP Device Manager call
- ** asynchronously. Does not wait for completion. Fails if not enough memory to
- ** create call object.
- **
- ** theCsCode (b_16): TCP operation code
- ** theParamBlockPtr (TCPiopb *): parameter block for TCP call
- **
- ** return (OSErr): result code (+1 is not returned)
- **
- */
-
- OSErr CTCPStream::DoAsyncCall (b_16 theCsCode, TCPiopb *theParamBlockPtr)
-
- {
- OSErr theResult;
- CTCPAsyncCall *theCall;
-
-
- // make sure a stream was opened
-
- if (macTCPStream)
- (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
- else
- return (invalidStreamPtr);
-
-
- // create call object
-
- theCall = new (CTCPAsyncCall);
- theCall->ITCPAsyncCall(this);
- if (theCsCode == TCPNoCopyRcv)
- theCall->SetRcvBypassProc(itsRcvBypassTarget, itsRcvBypassProc);
- theResult = theCall->DoAsyncCall(theCsCode, theParamBlockPtr);
- if (theResult == inProgress)
- itsAsyncCalls->Add(theCall);
- return ((theResult == inProgress) ? noErr : theResult);
- }
-
-
- /*______________________________________________________________________
- **
- ** DoSyncCall (protected method)
- **
- ** Fills in the standard parameters for a TCP Device Manager call and executes the call.
- ** Waits for completion of the call.
- **
- ** theCsCode (b_16): TCP operation code
- ** theParamBlockPtr (TCPiopb *): parameter block for TCP call
- **
- ** return (OSErr): result code (+1 is not returned)
- **
- */
-
- OSErr CTCPStream::DoSyncCall (b_16 theCsCode, TCPiopb *theParamBlockPtr)
-
- {
-
- // make sure a stream was opened
-
- if (macTCPStream)
- (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
- else
- return (invalidStreamPtr);
-
-
- // perform the call
-
- (*theParamBlockPtr).ioCompletion = (TCPIOCompletionProc) NULL;
- (*theParamBlockPtr).ioCRefNum = gTCPDriver->GetTCPRefNum();
- (*theParamBlockPtr).csCode = theCsCode;
- PBControlSync((ParmBlkPtr) theParamBlockPtr);
- return ((*theParamBlockPtr).ioResult);
- }
-
-
- // —— auto-receive processing ——
-
- /*______________________________________________________________________
- **
- ** IssueAutoReceive (protected method)
- **
- ** Issue a NoCopyRcv command to grab the next batch of data. Does nothing if auto-receive
- ** is not enabled.
- **
- */
-
- void CTCPStream::IssueAutoReceive (void)
-
- {
- Ptr newRDS; // the new RDS pointer
-
-
- // create a new RDS with room for # of entries requested & send it to TCP
-
- if (hasSessionOpen && itsAutoReceiveSize) {
- FailNIL(newRDS = NewPtr((itsAutoReceiveSize*6)+2));
- NoCopyRcv((rdsEntry *) newRDS, itsAutoReceiveSize, 0);
- }
- }
-
-
- // —— modified collaborator mechanism ——
-
- /*______________________________________________________________________
- **
- ** BroadcastSafeChange (protected method)
- **
- ** Same as CCollaborator::BroadcastChange, except it iterates over a copy of the
- ** dependents list. This allows the dependents to dispose of themselves in the iteration
- ** routine without wreaking havoc.
- **
- ** reason (long): the reason code to broadcast
- ** info (void *): additional information to pass along
- **
- */
-
- typedef struct tUpdateInfo {
- CCollaborator *provider;
- long reason;
- void *info;
- } tUpdateInfo;
-
-
- void CTCPStream::BroadcastSafeChange (long reason, void *info)
-
- {
- tUpdateInfo updateInfo;
- long nullInfo;
- CList *itsDependentsCopy;
-
-
- if (itsDependents) {
-
- // clone the dependents list
-
- itsDependentsCopy = (CList *) (itsDependents->Copy());
-
-
- // create a valid dereference for NULL info
-
- if (!info) {
- nullInfo = 0L;
- info = &nullInfo;
- }
-
-
- // iterate over the list
-
- updateInfo.provider = this;
- updateInfo.reason = reason;
- updateInfo.info = info;
- itsDependentsCopy->DoForEach1(CCollaborator::Dependent_Update,
- (long) &updateInfo);
- ForgetObject(itsDependentsCopy);
- }
-
- }
-
-
- // —— delayed-processing routines: respond to interrupt notifications
-
- /*______________________________________________________________________
- **
- ** ProcessNotify
- **
- ** Respond to notifications received by the ASR during interrupt time. This routine is free
- ** of interrupt-level constraints.
- **
- */
-
- void CTCPStream::ProcessNotify (void)
-
- {
- // no longer in notify queue
-
- pendingNotify = FALSE;
-
-
- // if terminated, inform the stream owner & dispose (if appropriate)
- // clear all other notifications (they no longer apply)
-
- if (notifTerminate) {
- HandleTerminated(notifTermReason);
- hasSessionOpen = pendingOpen = pendingClose =
- remoteClose = pendingAbort = notifClosing = notifTimeout =
- notifDataArrived = notifUrgent = FALSE;
- receivedClose = receivedTerminate = TRUE;
- if (disposeOnTerminate)
- PostponeDispose();
- }
-
-
- // if closed, inform stream owner, wait for termination before disposing (if appropriate)
-
- if (notifClosing) {
- HandleClosing(remoteClose);
- receivedClose = TRUE;
- notifClosing = FALSE;
- if (disposeOnTerminate)
- PostponeDispose();
- }
-
-
- // if urgent notification, flag beginning of urgent data
-
- if (notifUrgent) {
- if (!rcvUrgent) {
- RcvUrgentBegin();
- HandleUrgentBegin();
- }
- notifUrgent = FALSE;
- }
-
-
- // tell stream owner about other notifications, TurboTCP doesn’t respond to these
-
- if (notifTimeout) {
- HandleTimeout();
- notifTimeout = FALSE;
- }
-
- if (notifDataArrived) {
- HandleUnexpectedData();
- notifDataArrived = FALSE;
- }
-
- if (notifICMP) {
- HandleICMP(¬ifICMPreport);
- notifICMP = FALSE;
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** ProcessAsyncCompletion
- **
- ** Respond to the completion of an asynchronous call. Removes the async call object from
- ** the list of outstanding calls.
- **
- ** theCall (CTCPAsyncCall *): the call which was completed
- **
- */
-
- void CTCPStream::ProcessAsyncCompletion (CTCPAsyncCall *theCall)
-
- {
- itsAsyncCalls->CCluster::Remove(theCall);
- }
-
-
- /***********************************************************************
- ************************************************************************
- **
- ** INTERRUPT-LEVEL routines follow. These routines cannot make memory allocations, cannot make
- ** synchronous TCP calls, and cannot use the Think C profiler.
- **
- */
-
- #pragma options(!profile)
-
-
- /*______________________________________________________________________
- **
- ** PostponeDispose
- **
- ** Add this stream object to the delayed disposal queue. This routine may be called during
- ** an interrupt.
- **
- */
-
- void CTCPStream::PostponeDispose (void)
-
- {
- if (pendingDispose) // ignore this if we’re already in disposal queue
- return;
- pendingDispose = TRUE;
- disposeOnTerminate = TRUE;
- Enqueue((QElemPtr) &qDisposeEntry, &(gTCPDriver->asyncQueue));
- }
-
-
- /*______________________________________________________________________
- **
- ** PostponeNotify (protected method)
- **
- ** The asynchronous notification routine (ASR). Receives notification of events for all TCP
- ** streams. Dispatches the notification to the proper method of the CTCPStream object. This
- ** routine is installed for all TCP streams created by the CTCPStream object.
- **
- ** This method merely logs the kind of notification and adds this stream to the list of streams
- ** to be processed next time through the event loop.
- **
- ** eventCode (b_16): event code (see MacTCP manual, p37)
- ** terminReason (b_16): reason for termination (if applicable)
- ** icmpMsg (structICMPReport *): ICMP report (if applicable)
- **
- */
-
- void CTCPStream::PostponeNotify (b_16 eventCode, b_16 terminReason, struct ICMPReport *icmpMsg)
-
- {
-
- // reject notifications for unexpected data if auto-receiving
-
- if ((itsAutoReceiveSize > 0) && (eventCode == TCPDataArrival))
- return;
-
-
- // install this stream in asynchronous events queue
- // CTCPDriver::ProcessNetEvents will pick it up later
-
- if (!pendingNotify) {
- Enqueue((QElemPtr) &qNotifyEntry, &(gTCPDriver->asyncQueue));
- pendingNotify = TRUE;
- }
-
-
- // record the event type
-
- switch (eventCode) {
- case TCPClosing:
- remoteClose = remoteClose || !pendingClose;
- notifClosing = pendingClose = TRUE;
- break;
-
- case TCPULPTimeout:
- notifTimeout = TRUE;
- break;
-
- case TCPTerminate:
- notifTerminate = TRUE;
- notifTermReason = terminReason;
- break;
-
- case TCPDataArrival:
- notifDataArrived = TRUE;
- break;
-
- case TCPUrgent:
- notifUrgent = TRUE;
- break;
-
- case TCPICMPReceived:
- notifICMP = TRUE;
- BlockMove(icmpMsg, ¬ifICMPreport, 24);
- break;
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** NotifyProc (static protected method)
- **
- ** The asynchronous notification routine (ASR). This static method just decodes the
- ** userDataPtr and calls PostponeNotify for the object named. This routine is the standard
- ** ASR for all TCP streams created by the CTCPStream object.
- **
- ** tcpStream (StreamPtr): the TCP stream in question
- ** eventCode (short): TCP event code (see MacTCP Dev guide, p37)
- ** userDataPtr (Ptr): user data pointer (in this case, the CTCPStream)
- ** terminReason (short): reason for termination (see MacTCP Dev guide, p37)
- ** icmpMsg (…): ICMP report (if applicable)
- **
- */
-
- pascal void CTCPStream::NotifyProc (StreamPtr tcpStream, unsigned short eventCode,
- Ptr userDataPtr, unsigned short terminReason,
- struct ICMPReport *icmpMsg)
-
- {
- CTCPStream *theTCPStream = (CTCPStream *) userDataPtr;
- theTCPStream->PostponeNotify(eventCode, terminReason, icmpMsg);
- }
-